在這次鐵人賽的挑戰中,主要的目標是希望自己和讀者們可以更加認識 React.js 或者是複習一下以前學過的知識,所以安排了幾個單元來進行介紹,預期介紹的單元如下:
註: 本系列挑戰文會全部以 functional component 去做介紹
那在進入 React hooks 單元前,先來了解一下什麼是 hook?為什麼要使用 Hook?
在 React.js 的官網提到
Hook 是 React 16.8 中增加的新功能。它讓你不必寫 class 就能使用 state 以及其他 React 的功能。
React 從以前的 class component 轉向 function component 的設計其實就是往 FP 的方向去發展,但純粹的 function component 本身要渲染到網頁畫面上時無法去控制和記憶狀態,所以這個部分就透過 hooks 去做補足,換句話說就是讓 functional component 能夠擁有狀態,然後 functional component 和 React hooks 都以函式的形式撰寫,這樣的好處是開發者可以自由的定義函式的傳入參數和回傳值,在設計程式碼上更有彈性。
在過去,使用 class component 時需要使用 HOC(Higher Order Component) 與 Render Props 去進行 Code Reuse,但多層的 HOC 會造成 wrapper hell,維護性不佳,並且多層的 HOC 也可能會有 Props 命名衝突的問題。
class component 很多的程式碼也都和生命週期函式做綁定,如果有重複性邏輯會不好抽取出來,學習 JavaScript 的 class 語法也是一個拉高 React 學習的成本。
但我們現在可以將和某個小功能相關的程式碼邏輯放在同一個 custom hook 裡面,讓不同的元件需要該小功能時,就可以使用該 hook,減少程式碼的重複性,也更方便維護該功能。
每次呼叫一個 Hook 時,都會有一個完全獨立且隔離的 state,所以你甚至可以在一個 component 使用同一個客製化 Hook 兩次
看起來 hook 確實很強大,但它也有些使用上的限制:
這邊可以和 Day29 Fiber 段落的內容一起觀看,我們如果在元件內部使用了多個 hooks,它們的相關狀態、資料等會形成一個 linked list,每次渲染時就會依序照著這個 linked list 去執行 hook。
這樣設計的好處是 hooks 在多個元件複用時,可以不用擔心會有重複命名的問題,而是透過像是索引的方式去區別各個的 hooks,因此如果在條件式、迴圈內部或是巢狀函式等地方使用到 hooks,每次渲染就有可能不會執行到某些 hooks 進而導致順序錯亂。
在 React hooks: not magic, just arrays 這篇文章的 And the naive implementation 段落,有模擬一個簡化版本的 useState,作者提到這不是用來表達 hook 底層的運作方式,但簡化版本的 useState 可以更容易讓你了解為什麼 React hook 只能在 function component 裡的頂層調用的原因。
在範例程式碼中,使用了 state 和 setters 兩個陣列還有 cursor 計數的變數,而每使用一個 useState hook,就會將 state 和 setters 兩個陣列推入新的元素,然後 cursor 加一。所以 state 會分別存 firstName 和 lastName 值,setters 存 setFirstName 和 setLastName,此時每次渲染元件時,useState 的回傳值就是透過 cursor 當作索引去分別取出 state 和 setters 兩個陣列的元素,索引值不對就會更新到不正確的 state,這樣就很容易理解為什麼 hooks 會有這個限制的原因了。
const [firstName, setFirstName] = useState("Rudi"); // cursor: 0
const [lastName, setLastName] = useState("Yardley"); // cursor: 1
在我們了解 hook 是什麼及為什麼要使用它後,接著就來認識一下有哪些 hook 吧!